package c18类加载和反射;


import java.lang.reflect.*;
import java.lang.annotation.*;

/**
 * @author 炜sama
 * @@我的理解：假如通过类来产生对象为正向映射，即“正射”， 那通过对象反过来得到类或接口可以理解为“反射”。
 */
public class s20利用反射步骤 {
	public static void main(String[] args)
			throws Exception {
		/**
		 * 步骤1、获取 java.lang.Class 对象：有4种方式，各有各的特点
		 */
		//1.安全、性能好，但是必须要有类文件
		Class<TestFieldName> ct1 = TestFieldName.class;
		//2.适用于只有一个类名字符串时
		Class<?> ct21 = Class.forName("c18类加载和反射.Test"); //必须用全类名. 获得的 class 已完成初始化
		Class<?> ct22 = ClassLoader.getSystemClassLoader()
				.loadClass("c18类加载和反射.Test"); //必须用全类名. 获得的 class 只完成了加载, 还没链接和初始化
		//3.适合于运行时动态获得Class对象
		Class<? extends TestFieldName> ct3 = new TestFieldName().getClass();

		/**
		 * 步骤2、根据Class对象获取类的各种成员（构造器、Field、方法等）并访问它们。规律：
		 * 1、getXxx(形参)为返回指定的Xxx，getXxxs()为返回所有符合条件的Xxx--Xxx 指构造方法,属性,方法等, 下同。
		 * 2、一般带有 Declared 的是返回所有访问权限的 Xxx；否则只返回 public 的。
		 * 3、如果要访问私有成员，要先调用成员的 setAccessible(true) 方法，关闭访问权限检查；否则出异常！
		 * 4、getGenericXxx 表示返回泛型的 Xxx（如果有泛型的话）。
		 * 5、Constructor、Field、Method 都继承或实现了一些同样的类或接口，所以它们有很多同样的方法，
		 * 		掌握这些方法可以事半功倍！
		 *
		 * 按住Ctrl+鼠标左键点击类名分别查看以下 demo：
		 */
		s21利用反射获取并操作构造器 t1;
		s22利用反射获取并操作属性 t2;
		s23利用反射获取并调用操作方法 t3;

		/**
		 * 继承于接口 AnnotatedElement 的方法
		 */
		//返回此元素上存在的所有注释。
		for (Annotation an : ct1.getDeclaredAnnotations()) {
			System.err.println(an);
		}
		System.err.println("-----------------");
		//返回直接存在于此元素上的所有注释。
		for (Annotation an : ct1.getAnnotations()) {
			System.err.println(an);
		}
		System.err.println(ct1.getAnnotation(Deprecated.class));
		//下面返回null！这是因为@SuppressWarnings使用了Retention(value=SOURCE)修饰，表明它只能保存在
		//源码级别，而ct1是运行时对象，所以无法访问到@SuppressWarnings注解！
		System.err.println("该Class元素上的@SuppressWarnings注释为："
				+ ct1.getAnnotation(SuppressWarnings.class));
		System.err.println(ct1.isAnnotationPresent(Deprecated.class));

		/**
		 * 内、外部类；接口、父类
		 */
		//获取该Class对象所对应类的全部内部类：ed
		Class<?>[] inners = ct1.getDeclaredClasses();//复数，内部类可以有多个
		System.err.println("Test的全部内部类如下：");
		for (Class c : inners) {
			System.err.println(c);
		}
		//使用Class.forName方法加载ClassTest的Inner内部类
		Class inct1 = Class.forName("c18类加载和反射.Test$Inner");
		//通过getDeclaringClass()访问该类所在的外部类：ing
		System.err.println("inct1对应类的外部类为：" +
				inct1.getDeclaringClass());//不是复数！外部类只有一个
		System.err.println("继承的父类或实现的接口：");
		for (Class<?> c : ct1.getInterfaces()) {
			System.err.println(c);
		}
		System.err.println("Test的父类为：" + ct1.getSuperclass());

		/**
		 * 修饰符、类名、包等
		 */
		System.err.println("修饰符：" + Modifier.toString(ct1.getModifiers()));
		System.err.println("全类名：" + ct1.getName());
		System.err.println("简单类名：" + ct1.getSimpleName());
		System.err.println("Test的包为：" + ct1.getPackage());

		/**
		 * 判断类是神马
		 */
		System.err.println("Test是注解？" + ct1.isAnnotation());
		System.err.println("Test是匿名内部类？" + ct1.isAnonymousClass());
		System.err.println("Test是数组？" + ct1.isArray());
		System.err.println("Test是枚举？" + ct1.isEnum());
		System.err.println("Test是接口？" + ct1.isInterface());
		System.err.println("Test是基本类型？" + ct1.isPrimitive());
		System.err.println("该匿名对象是否ct1的实例？" + ct1.isInstance(new TestFieldName()));//可代替instanceof
		//判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，或是否是其超类或超接口
//		System.err.println("Test是否该类的超类或超接口或相同？"
//				//a.isAssignableFrom(b): a 是否可以被 b 赋值, 即 a=b 是否成立? 如果可以, 那 a 就是 b 的 super 或同类
//				+ct1.isAssignableFrom(TestFieldName.Inner2.class)
//		);
		System.err.println("Test是本地类？" + ct1.isLocalClass());//??定义在方法里的类，类似于本地变量
		System.err.println("Test是成员类？" + ct1.isLocalClass());//??定义在类里的类，类似于成员变量
		System.err.println("Inner是成员类？" + inct1.isLocalClass());//??定义在类里的类，类似于成员变量

		/**
		 * 步骤2.1：与Class成员无关的部分
		 */
		s24利用反射操作数组 t21;
	}
}

